using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace MatrixXna
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        SpriteFont sf;

        public static int Linelength = 30;
        bool keyIsDown = false;
        bool IsRed = false;

        List<MatrixLine> Lines;

        public static List<string> myNumbers = new List<string>();
        public static List<Color> myColors = new List<Color>();

        int Updates = 0;
        double updateTime = 0;

        public Game1()
        {
            base.IsFixedTimeStep = true;
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            graphics.PreferredBackBufferHeight = 800;
            graphics.PreferredBackBufferWidth = 1800;
            this.IsMouseVisible = true;
            graphics.ApplyChanges();

            Lines = new List<MatrixLine>();
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            base.Initialize();
            base.IsFixedTimeStep = true;
            base.TargetElapsedTime = new TimeSpan(100000L);
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {

            Random r = new Random();
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            sf = Content.Load<SpriteFont>("consoleFont");

            LoadColors();
            LoadNumbers();

            Vector2 startPos = new Vector2(0,0);

            while (startPos.X < 1800)
            {
                startPos.X += r.Next(10, 50);
                startPos.Y = (r.Next(2000) - 1000);
                MatrixLine NewLine = new MatrixLine(sf, startPos);
                Lines.Add(NewLine);
            }
            
        }


        private void LoadColors()
        {
            int transparency = 0;
            for (int i = 0; i < Linelength; i++)
            {
                transparency = i * 17;
                if (transparency > 255) transparency = 255;
                myColors.Add(new Color(20, 120, 20, (byte)transparency));
            }
        }

        private void LoadNumbers()
        {
            Random r = new Random();

            for (int s = 0; s < 1900; s++)
            {
                myNumbers.Add(char.ConvertFromUtf32( r.Next(32, 999) ));
            }
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            Lines = null;
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            UpdateInput(gameTime);

            foreach (MatrixLine i in Lines)
            {
                i.Update(gameTime);
            }
            
            base.Update(gameTime);

            Updates++;

            updateTime += gameTime.ElapsedRealTime.TotalMilliseconds;

            if (updateTime > 1000)
            {
                updateTime = 0;
                this.Window.Title = Updates.ToString();
                Updates = 0;
            }

            
        }

        void UpdateInput(GameTime gameTime)
        {
            if (Mouse.GetState().RightButton == ButtonState.Pressed) this.Exit();

            if (!keyIsDown)
            {
                if (Keyboard.GetState().IsKeyDown(Keys.F))
                {
                    keyIsDown = true;
                    graphics.ToggleFullScreen();
                }

                else if (Keyboard.GetState().IsKeyDown(Keys.C))
                {
                    if (!IsRed)
                    {
                        for (int i = 0; i < myColors.Count; i++)
                        {
                            myColors[i] = new Color(120, 20, 20, myColors[i].A);
                        }
                        IsRed = true;
                    }
                    else
                    {
                        for (int i = 0; i < myColors.Count; i++)
                        {
                            myColors[i] = new Color(20, 100, 20, myColors[i].A);
                        }
                        IsRed = false;
                    }
                }
                
            }

            else if (Keyboard.GetState().IsKeyUp(Keys.F)) keyIsDown = false;
            else if (Keyboard.GetState().IsKeyUp(Keys.C)) keyIsDown = false;
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            spriteBatch.Begin(SpriteBlendMode.AlphaBlend);

            foreach (MatrixLine i in Lines)
            {
                i.Draw(spriteBatch);
            }

            spriteBatch.End();

            base.Draw(gameTime);
        }
    }

    class MatrixLine
    {
        private SpriteFont font;

        public float StartPos { get; set; }
        public Vector2 Pos = new Vector2(0, 0);

        public MatrixLine(SpriteFont sf, Vector2 StartPosition)
        {
            font = sf;
            Pos = StartPosition;
            StartPos = StartPosition.Y;
        }

        public void Update(GameTime GTime)
        {
            float TimeElapsed = (float)(GTime.ElapsedRealTime.TotalMilliseconds / 10);

            StartPos += TimeElapsed;

            if (StartPos > 1000)StartPos = -1000;

            Pos.Y = StartPos;
            
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            for (int i = 0; i < Game1.Linelength; i++)
            {
                spriteBatch.DrawString(font, Game1.myNumbers[i + (int)Pos.X], Pos, Game1.myColors[i]);
                Pos.Y += 20;
            }
        }
    }   
}
